package com.pnemec.addlog;

/**
 * Read XML files which describe which files are need to provide to bugzilla. 
 * TODO :  
 *  - translate ~ into home directory-
 *  - add stop section (when run is evaluated an some one set shouldStop to true 
 * 
 * @author Pavel Němec <nemecp4@gmail.com>
 *
 */
import java.awt.Image;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.pnemec.swing.SimpleEventAdapter;
import com.pnemec.swing.SimpleEventListener;
import com.pnemec.util.DataProvider;
import com.pnemec.util.Debug;

public class LogXmlReader implements LogModule, Runnable, SimpleEventAdapter {
	private String path = null;

	private Document document = null;

	/* thread in which this class is executed */
	private Thread thread = null;

	private HashMap infoHM = null;

	private LinkedList files = null, suFiles = null, commands = null;
	private LinkedList outputFileNames = null;

	/*
	 * listeners of this class, when something happned all items in this list
	 * will be notified
	 */
	private LinkedList eventListeners = null;

	private int resultType = -1;

	private String status = "not started";

	/* if currently run method is in progress */
	private boolean isRunning = false;

	/* TODO implement in run method stop if this flag is raised */
	private boolean shouldStop = false;

	/* if method run was runned or not */
	private boolean isStarted = false;

	/* zip containing files and output */
	private File zip = null;

	private String textResult = null;

	private StringBuffer textOutput = null;

	/* gui stuff */
	private ImageIcon icon = null;
	
	/* currently running process */
	private Process p = null;

	public LogXmlReader(String path) {
		System.out.println("LogXmlReader.constructor: "+path);
		this.path = path;
		infoHM = new HashMap();
		init();
		this.resultType=DataProvider.DEFAULT_RESULT_TYPE;
		status="Not Started";
	}

	private void init() {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

		DocumentBuilder builder;
		try {
			builder = factory.newDocumentBuilder();
//			InputStream is = this.getClass().getResourceAsStream(path);
			InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
			BufferedInputStream stream = new BufferedInputStream(is);
			document = builder.parse(stream);

			/* parse info */
			NodeList nl = (document.getElementsByTagName("info")).item(0)
					.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				//System.out.println("node: "+node.getNodeName()+", content: "+node.getTextContent());
				infoHM.put(node.getNodeName(), node.getTextContent());
			}
			stream.close();
			is.close();

		} catch (ParserConfigurationException e) {
			System.out
					.println("ConfigurationReader.init(): cannot parse document: "
							+ e.getMessage());

		} catch (SAXException e) {
			System.out
					.println("ConfigurationReader.init(): cannot parse document (SAX exeption): "
							+ e.getMessage());
		} catch (IOException e) {
			System.out.println("ConfigurationReader.init(): cannot open file: "
					+ (this.path == null ? "null" : this.path) + ", message: "
					+ e.getMessage());
			e.printStackTrace();
		}
	}

	private void fireEvent(String newStatus){
		status=newStatus;
		if(eventListeners!=null)
			for (Iterator iterator = eventListeners.iterator(); iterator.hasNext();) {
				((SimpleEventListener) iterator.next()).notifyEvent(this);
			}
	}
	
	/**
	 * return list of Strings
	 * 
	 * @return
	 */
	public Collection getFiles() {
		if (files == null) {
			NodeList list = document.getElementsByTagName("files");
			for (int i = 0; i < list.getLength(); i++) {
				Node node = list.item(i);
				Node n = node.getAttributes().getNamedItem("su");
				if (n == null || n.getNodeValue().compareTo("true") == -1) {
					if (files == null)
						files = new LinkedList();
					NodeList childrens = node.getChildNodes();
					for (int j = 0; j < childrens.getLength(); j++) {
						n = childrens.item(j);
						if (n.getNodeName().compareTo("file") == 0)
							files.add(n.getTextContent());
					}
				}
			}
		}
		return files;
	}

	public Collection getSuFiles() {
		if (suFiles == null) {
			NodeList list = document.getElementsByTagName("files");
			for (int i = 0; i < list.getLength(); i++) {
				Node node = list.item(i);
				Node n = node.getAttributes().getNamedItem("su");
				if (n != null && n.getNodeValue().compareTo("true") == 0) {
					if (suFiles == null)
						suFiles = new LinkedList();
					NodeList childrens = node.getChildNodes();
					for (int j = 0; j < childrens.getLength(); j++) {
						n = childrens.item(j);
						if (n.getNodeName().compareTo("file") == 0)
							suFiles.add(n.getTextContent());
					}
				}
			}
		}
		return suFiles;
	}

	public Collection getCommands() {
		if (commands == null) {
			NodeList list = document.getElementsByTagName("output");
			for (int i = 0; i < list.getLength(); i++) {
				Node node = list.item(i);
				if (commands == null)
					commands = new LinkedList();
				NodeList childrens = node.getChildNodes();
				for (int j = 0; j < childrens.getLength(); j++) {
					if (childrens.item(j).getNodeName().compareTo("command") == 0)
						commands.add(childrens.item(j).getTextContent());
				}

			}
		}
		return commands;
	}

	public Collection getAccesedFiles() {
		LinkedList reti = new LinkedList();
		Collection tmp = getFiles();
		if (tmp != null)
			for (Iterator i = tmp.iterator(); i.hasNext();)
				reti.add(i.next());
		tmp = getSuFiles();
		if (tmp != null)
			for (Iterator i = tmp.iterator(); i.hasNext();)
				reti.add(i.next());
		tmp = getCommands();
		if (tmp != null)
			for (Iterator i = tmp.iterator(); i.hasNext();)
				reti.add(i.next());

		return reti;
	}

	public String getName() {
		return infoHM.containsKey("name") ? (String) infoHM.get("name")
				: "unknown";
	}

	public String getDescription() {
		return (String) infoHM.get("description");
	}

	public String getSummary() {
		return infoHM.containsKey("name") ? (String) infoHM.get("summary")
				: "unknown";
	}

	public JPanel getGui() {
		// TODO Auto-generated method stub
		return null;
	}

	public String getStatus() {
		return status;
	}

	public boolean isRunning() {
		return (this.isRunning) && (thread != null) && (thread.isAlive());
	}

	public void setType(int type) {
		Debug.log("LogXmlReader.setType: setting type to "+type);
		this.resultType = type;
	}

	public void startGatherLogs() {
		if (this.isRunning == true || (thread != null && thread.isAlive())) {
			System.out
					.println("LogXmlReader.startGatherLogs(): allready started, not finish");
			return;
		}
		thread = new Thread(this);
		thread.start();
	}

	public synchronized void stopGatherLogs() {
		this.shouldStop = true;
		if(p!=null)p.notify();

	}

	public void run() {
		isStarted = true;
		isRunning = true;
		fireEvent("Started");
		Runtime runtime = Runtime.getRuntime();

		BufferedReader br = null;
		BufferedWriter bw = null;
		Debug.log(this.getName()+" starting with result type: "+this.resultType);
		if (this.resultType == TYPE_ALL_ZIPPED_UPLOAD)
			zip = DataProvider.getGlobalTarName();
//		else {
//			if (this.resultType == TYPE_MULTIPLE_ZIPPS)
//				zip = DataProvider.getTarName(getName());
//		}
		if (this.resultType == TYPE_ALL_ZIPPED_UPLOAD
				|| this.resultType == TYPE_MULTIPLE_ZIPPS) {
			outputFileNames = new LinkedList();
		} else
			outputFileNames = null;

		/* If we need output in text form */
		if (this.resultType == TYPE_TEXT_SAVE_AS_FILE) {
			/* store output into StringBuffer - hope it is big enought */
			Collection files = getFiles();
			String line;
			String element;
			textOutput = new StringBuffer();
			fireEvent("Collecting files");
			for (Iterator iter = files.iterator(); iter.hasNext();) {
				element = (String) iter.next();
				textOutput.append("##############################\n");
				textOutput.append("# output of file: " + element + "\n");
				try {
					br = new BufferedReader(new FileReader(element));
					while ((line = br.readLine()) != null)
						textOutput.append(line + "\n");
					br.close();
					br = null;

				} catch (FileNotFoundException e) {
					Debug.log("LogXmlReader.run: cannot read file: "
							+ (element != null ? element : "null"));
				} catch (IOException e) {
					/* TODO : i should check br is closed? */
					Debug
							.log("LogXmlReader.rung: IOExeption when reading from file: "
									+ (element != null ? element : "null"));
				}
			}
			Collection cmds = getCommands();
			/* run command, collect output */
			fireEvent("Running commands");
			for (Iterator iter = cmds.iterator(); iter.hasNext();) {

				String cmd;
				cmd = (String) iter.next();
				textOutput.append("Output of commad:\n" + cmd + "\n");
				try {
					status = "Exectuning command at: "
							+ System.currentTimeMillis();
					Debug.log(this.getName() + " executing command: "+cmd);
					p = runtime.exec(cmd);
					try {
						p.waitFor();
						
					} catch (InterruptedException ie) {
						Debug.log("LogXmlReader.run: interrupted");
					}
					br = new BufferedReader(new InputStreamReader(p
							.getInputStream()));
					textOutput.append("return value: " + p.exitValue() + "\n");
					while ((line = br.readLine()) != null)
						textOutput.append(line + "\n");
					br.close();
					/*
					 * } else { textOutput.append("calling command " + cmd + "
					 * fail"); }
					 */
					Debug.log(this.getName() + " command finished");
					p = null;
				} catch (IOException e) {
					textOutput.append("Fail\n");
					Debug.log("LogXmlReader.run: "
							+ "IOEexpetion when running command: " + cmd);
				} finally {
					if (p != null) {
						p.destroy();
						p = null;
					}
				}

			}
			textResult = textOutput.toString();
		}// end of text output
		else {
			/* pack files into tar */
			 DataProvider.addFilesToTar(zip, getFiles());
			 
			// DataProvider.addFilesToTar(zip, getSuFiles()); - this do not work
			// yet !
			/* pack output of commands into tar */
			fireEvent("Collecting files");
			for (Iterator iter = getCommands().iterator(); iter.hasNext();) {
				try {
					String singleCmd = (String) iter.next();
					Debug.log(this.getName() + " executing single command: "+singleCmd);
					try {
						p = runtime.exec(singleCmd);
						p.waitFor();
					} catch (InterruptedException e) {
						Debug
								.log("LogXmlReader.run: interrupted during tarring of files");
					}

					/* store output to file with name like 'hwinfo_--gfxcard' */
					String fileName = singleCmd.replaceAll(" ", "_");
					fileName = fileName.replaceAll("/", ".");
					File logF = DataProvider.getTemporaryFile(fileName);
					outputFileNames.add(logF.getPath());
					bw = new BufferedWriter(new FileWriter(logF));
					if (p != null) {

						br = new BufferedReader(new InputStreamReader(p
								.getInputStream()));
						String line;
						bw.write("Output of command:\n" + singleCmd
								+ "\nreturn value: " + p.exitValue() + "\n");
						while ((line = br.readLine()) != null)
							bw.write(line + "\n");
						bw.close();

					} else {
						bw.write("Output of command:\n" + singleCmd + "\n");
						bw.write("FAIL to execute");
						bw.close();
					}
					p = null;
					Debug.log(this.getName() + " command finished");
				} catch (IOException e) {
					Debug
							.log("LogXmlReader.run: IOException during running of files");
					e.printStackTrace();
				} finally {// clean up after every command
					if (br != null) {
						try {
							br.close();
						} catch (IOException ioe) {/* throw now */
						}
						br = null;
					}
					if (bw != null) {
						try {
							bw.close();
						} catch (IOException ioe) {/* throw now */
						}
						bw = null;
					}
					if (p != null) {
						p.destroy();
						p = null;
					}

				}

			}// end of tar output
			fireEvent("Adding to zip");
			DataProvider.addFilesToTar(zip, outputFileNames);
		}
		Debug.log("LogXmlReader<"+this.getName()+">.run() finishedl");
		this.isRunning = false;
		fireEvent("Finished");
	}

	public File getTarResult() throws Exception {
		if (this.resultType == LogModule.TYPE_ALL_ZIPPED_UPLOAD
				|| this.resultType == LogModule.TYPE_MULTIPLE_ZIPPS)
			return this.zip;
		else {
			throw new Exception("LogXmlReader.getTarResult(): wrong type"
					+ this.resultType + ", expecting: "
					+ LogModule.TYPE_ALL_ZIPPED_UPLOAD + " or "
					+ LogModule.TYPE_MULTIPLE_ZIPPS);
		}
	}

	public String getTextResult() throws Exception {
		if (this.resultType == LogModule.TYPE_TEXT_SAVE_AS_FILE)
			return textResult;
		else {
			throw new Exception("LogXmlReader.getTextResult(): wrong type"
					+ this.resultType + ", expecting: " + LogModule.TYPE_TEXT_SAVE_AS_FILE);
		}
	}

	public int getType() {
		// TODO Auto-generated method stub
		return 0;
	}

	public int getEventValue() {
		if (!this.isStarted)
			return 1;
		if (this.isRunning)
			return 2;
		return 3;
	}

	/**
	 * return icon for this class
	 * 
	 * @return Icon - icon if right icon is found null otherwise
	 */
	public Icon getIcon() {
		if (icon != null)
			return icon;
		if (infoHM.containsKey("icon")) {
			/* image is cached on data provider side ! */
			Image image = DataProvider.getImage((String) infoHM.get("icon"));
			if(image!=null){icon = new ImageIcon(image);
				return icon;
			}
		}
		return null;
	}

	public void addEventListener(SimpleEventListener listener) {
		if (eventListeners == null)
			eventListeners = new LinkedList();
		eventListeners.add(listener);

	}
}
